Coworking Spaces¶
Authored by: Linh Huong Nguyen
Duration: 90 mins
Level: Intermediate
Pre-requisite Skills: Python, Pandas, Matplotlib, NumbPy, Seaborn, Scikit-learn
Scenario¶
As a remote worker, freelancer, or entrepreneur in Melbourne, I want to easily discover coworking spaces in Melbourne, so that I can quickly find a suitable workspace near me that meets my needs and supports local businesses.
As a city planner or coworking space investor, I want to identify optimal locations for new coworking spaces based on proximity to local businesses and public transport, so that I can make data-driven decisions that enhance accessibility and community integration.
What this use case will teach you¶
At the end of this use case, you will have demonstrated the following skills:
Extracted and imported datasets from open data portals for analysis using APIs.
Conducted data cleaning and preprocessing to ensure data quality and consistency.
Performed exploratory data analysis (EDA) to uncover trends, patterns, and anomalies.
Utilised clustering algorithms to segment regions or data points based on relevant features.
Applied advanced machine learning and statistical methods to evaluate clustering outcomes and derive meaningful insights.
Background and Introduction¶
With the rise of remote work and flexible professional lifestyles, coworking spaces have become essential infrastructure in modern cities. In Melbourne, the growing community of freelancers, remote workers, and entrepreneurs is increasingly seeking convenient, well-equipped spaces to work, collaborate, and connect. At the same time, city planners and investors are exploring opportunities to support this shift by improving accessibility and identifying optimal locations for new coworking developments.
This use case presents an interactive mapping solution that visualises the distribution of coworking spaces across Melbourne. The map enables users to easily discover existing coworking hubs, along with nearby bus stops, trams stops and local businesses such as cafés, convenience stores, and gyms. By layering these data points, the tool serves two key audiences:
Remote workers and freelancers can quickly find coworking spaces that are conveniently located and well-supported by nearby services.
City planners and coworking investors can identify high-potential areas for expansion based on business density and public transport accessibility.
By integrating spatial data of coworking spaces, business establishments, tram stops and bus stops, this tool supports both individual productivity and strategic urban development, fostering a more connected and vibrant coworking ecosystem in Melbourne.
Datasets used¶
Coworking Spaces
This dataset contains the listing of coworking spaces within the City of Melbourne municipal boundary, with their trading names, addresses and precise location. This dataset is sourced from the Melbourne Open Data website and can be accessed via API V2.1.Business Establishments Location and Industry Classification
This dataset details the locations and industry classifications of businesses in Melbourne, offering insights into existing retail density and types of businesses operating in specific areas. This dataset is sourced from the Melbourne Open Data website using API V2.1.Bus stops
This data set shows the locations of the bus stops within the city of Melbourne. This dataset is sourced from the Melbourne Open Data website using API V2.1.City Circle tram stops
The City Circle tram service operates within Melbourne's central business district. The service operates in a circular route passing major tourist attractions, as well as linking with other tram, train and bus routes in and around Melbourne. This dataset shows the location of the stops on the route. The dataset is sourced from the Melbourne Open Data website using API V2.1.
Importing Datasets¶
This section imports essential libraries for data manipulation, visualisation, geospatial analysis, interactive mapping, and fetching data from APIs. These libraries provide the necessary functionality for processing, analysing, and visualising the project data effectively.
import requests
import pandas as pd
import os
import folium
from io import StringIO
import matplotlib.pyplot as plt
from folium.plugins import MarkerCluster
from folium.plugins import FeatureGroupSubGroup
Loading the datasets using API 2.1v¶
This section defines functions for fetching data from APIs. The API_Unlimited function retrieves datasets from the Melbourne Open Data Portal using dataset IDs, processes the data into a DataFrame, and provides a preview for verification. Similarly, the fetch_data_from_url function fetches data directly from a given URL, processes it into a DataFrame, and displays a sample for validation. These functions enable seamless access to external datasets for analysis.
def API_Unlimited(datasetname): # pass in dataset name and api key
dataset_id = datasetname
base_url = 'https://data.melbourne.vic.gov.au/api/explore/v2.1/catalog/datasets/'
#apikey = api_key
dataset_id = dataset_id
format = 'csv'
url = f'{base_url}{dataset_id}/exports/{format}'
params = {
'select': '*',
'limit': -1, # all records
'lang': 'en',
'timezone': 'UTC'
}
# GET request
response = requests.get(url, params=params)
if response.status_code == 200:
# StringIO to read the CSV data
url_content = response.content.decode('utf-8')
datasetname = pd.read_csv(StringIO(url_content), delimiter=';')
print(datasetname.sample(10, random_state=999)) # Test
return datasetname
else:
return (print(f'Request failed with status code {response.status_code}'))
Fetching and Previewing Datasets¶
This section defines the dataset download links required for the use case and fetches the corresponding data using the API_Unlimited function. The datasets include coworking spaces, business establishment details, tram stops and bus stops which are essential for creating a map of all coworking spaces and nearby amenities. After retrieval, the code displays the first few rows of each dataset to confirm successful loading and ensure data integrity.
download_link_1 = 'coworking-spaces'
download_link_2 ='business-establishments-with-address-and-industry-classification'
download_link_3 = 'city-circle-tram-stops'
download_link_4 = 'bus-stops'
# Use functions to download and load data
coworking_df = API_Unlimited(download_link_1)
business_df = API_Unlimited(download_link_2)
tram_df = API_Unlimited(download_link_3)
bus_df = API_Unlimited(download_link_4)
organisation address \
43 Regus 367 Collins Street, Melbourne 3000
34 Christie Spaces 454 Collins St, Melbourne VIC 3000
18 workspace365 607 Bourke Street, Melbourne 3000
42 Regus 385 Bourke Street, Melbourne 3000
22 Hub Southern Cross: 696 Bourke Street, Melbourne 3000
38 GPT Space&Co Level 1 and 2, 376-390 Collins Street, Melbour...
24 Regus 430 Little Collins Street, Melbourne 3000
3 GPT Space&Co 181 William Street, Melbourne VIC 3000
32 11th Space 11/568 Collins St, Melbourne VIC 3000
29 Startspace HQ State Library of Victoria 328 Swanston Street ...
website latitude longitude \
43 https://www.regus.com.au/offices/australia/vic... -37.817000 144.962000
34 https://www.christiespaces.com.au/spaces/ -37.817329 144.959269
18 https://www.workspace365.com.au/co-working -37.817000 144.956000
42 https://www.regus.com.au/offices/australia/vic... -37.815000 144.963000
22 http://www.hubaustralia.com/ -37.817000 144.954000
38 https://www.gpt.com.au/office/space-co -37.816468 144.961865
24 https://www.regus.com.au/offices/australia/vic... -37.816000 144.960000
3 https://www.gpt.com.au/office/space-co -37.815000 144.958000
32 https://www.11thspace.com/ -37.818537 144.955502
29 https://startspacehq.com/ -37.809747 144.965485
geopoint
43 -37.81694, 144.96233
34 -37.8173293, 144.9592691
18 -37.816673, 144.955505
42 -37.81455, 144.96293
22 -37.8166157, 144.9535632
38 -37.81646826110876, 144.96186456818185
24 -37.81609, 144.9602
3 -37.81509510253944, 144.9576562644202
32 -37.8185368, 144.9555017
29 -37.8097471, 144.9654847
census_year block_id property_id base_property_id \
330165 2008 13 110151 110151
383606 2022 1109 601815 601815
130289 2005 35 102141 102141
346935 2022 43 101152 101152
213086 2021 2392 617402 617402
154214 2017 93 105477 105477
166795 2023 334 106060 106060
126599 2007 430 109015 109015
152632 2017 42 105950 105950
220560 2019 806 110343 640947
clue_small_area trading_name \
330165 Melbourne (CBD) Museum Victoria
383606 Docklands The Frame Group Pty Limited
130289 Melbourne (CBD) Edesktop.com.au
346935 Melbourne (CBD) Officeworks
213086 North Melbourne Melrose Takeaway
154214 Melbourne (CBD) Vix Technology (Aust) Pty Ltd
166795 North Melbourne Vacant
126599 West Melbourne (Residential) Spencer Panels Pty Ltd
152632 Melbourne (CBD) City Physio.com.au
220560 Southbank Kingpin Bowling Crown / Playtime
business_address \
330165 Part Level 4, 22 William Street MELBOURNE 3000
383606 Suite 1508, Level 15 401 Docklands Drive DOCKL...
130289 Part Mezzanine 0, 290-0 Collins Street MELBOUR...
346935 Part Ground 461 Bourke Street MELBOURNE VIC 3000
213086 35 Melrose Street NORTH MELBOURNE VIC 3051
154214 Level 21, 380-390 La Trobe Street MELBOURNE 3000
166795 12 Little Lothian Street North NORTH MELBOURNE...
126599 Part 143-153 Stanley Street WEST MELBOURNE 3003
152632 Suite 316, 530 Little Collins Street MELBOURNE...
220560 Shop 120, Gnd & Mezz 27 Whiteman Street SOUTHB...
industry_anzsic4_code industry_anzsic4_description \
330165 8910 Museum Operation
383606 5809 Other Telecommunications Services
130289 7000 Computer System Design and Related Services
346935 4272 Stationery Goods Retailing
213086 4512 Takeaway Food Services
154214 7000 Computer System Design and Related Services
166795 0 Vacant Space
126599 9412 Automotive Body, Paint and Interior Repair
152632 8533 Physiotherapy Services
220560 9131 Amusement Parks and Centres Operation
longitude latitude point
330165 144.959965 -37.818777 -37.81877676610428, 144.9599652269
383606 144.940212 -37.814288 -37.814288099349994, 144.9402116381848
130289 144.964579 -37.815631 -37.81563097004982, 144.96457927162226
346935 144.960173 -37.815492 -37.81549193230968, 144.9601728808
213086 144.941404 -37.795017 -37.79501732425005, 144.9414035963
154214 144.958051 -37.810913 -37.81091259571215, 144.95805069635
166795 144.945153 -37.802211 -37.80221069838089, 144.94515310974998
126599 144.947545 -37.809530 -37.80952975596672, 144.94754472729133
152632 144.957110 -37.816880 -37.8168797463, 144.95710995453825
220560 144.958442 -37.825404 -37.825404033, 144.95844223381087
geo_point_2d \
10 -37.82097269970027, 144.95546153614245
12 -37.811771476718356, 144.95644059700524
27 -37.81667338583987, 144.97015587085124
18 -37.818324403770184, 144.964479208357
3 -37.813414856197724, 144.94137823870162
23 -37.810354377085794, 144.96136850025573
20 -37.81865571347738, 144.94650837136655
9 -37.82023778673241, 144.95786314283018
15 -37.808876998255194, 144.96634474519394
6 -37.8081489607039, 144.96879323779422
geo_shape \
10 {"coordinates": [144.95546153614245, -37.82097...
12 {"coordinates": [144.95644059700524, -37.81177...
27 {"coordinates": [144.97015587085124, -37.81667...
18 {"coordinates": [144.964479208357, -37.8183244...
3 {"coordinates": [144.94137823870162, -37.81341...
23 {"coordinates": [144.96136850025573, -37.81035...
20 {"coordinates": [144.94650837136655, -37.81865...
9 {"coordinates": [144.95786314283018, -37.82023...
15 {"coordinates": [144.96634474519394, -37.80887...
6 {"coordinates": [144.96879323779422, -37.80814...
name xorg stop_no mccid_str \
10 Spencer Street / Flinders Street GIS Team 1 NaN
12 William Street / La Trobe Street GIS Team 3 NaN
27 Russell Street / Flinders Street GIS Team 6 NaN
18 Elizabeth Street / Flinders Street GIS Team 4 NaN
3 New Quay Promenade / Docklands Drive GIS Team D10 NaN
23 Elizabeth Street / La Trobe Street GIS Team 5 NaN
20 Bourke Street / Harbour Esplanade GIS Team D3 NaN
9 Melbourne Aquarium / Flinders Street GIS Team 2 NaN
15 Russell Street / La Trobe Street GIS Team 7 NaN
6 Exhibition Street / La Trobe Street GIS Team 8 NaN
xsource xdate mccid_int
10 Mapbase 2011-10-18 5
12 Mapbase 2011-10-18 16
27 Mapbase 2011-10-18 28
18 Mapbase 2011-10-18 2
3 Mapbase 2011-10-18 11
23 Mapbase 2011-10-18 18
20 Mapbase 2011-10-18 9
9 Mapbase 2011-10-18 4
15 Mapbase 2011-10-18 20
6 Mapbase 2011-10-18 21
geo_point_2d \
293 -37.78737016259562, 144.96918092237397
8 -37.837547087144706, 144.98191138368836
30 -37.82480198399865, 144.97076232908503
308 -37.818314889062094, 144.956839508202
289 -37.81105987177411, 144.95869339408225
109 -37.78077459328419, 144.95138857277198
45 -37.79443959174042, 144.9295031556217
243 -37.803343440196116, 144.9693670992385
273 -37.80282843793904, 144.9479395483275
135 -37.80111524772101, 144.96674878780823
geo_shape prop_id addresspt1 \
293 {"coordinates": [144.96918092237397, -37.78737... 0 0.000000
8 {"coordinates": [144.98191138368836, -37.83754... 0 41.441167
30 {"coordinates": [144.97076232908503, -37.82480... 0 26.353383
308 {"coordinates": [144.956839508202, -37.8183148... 0 35.877984
289 {"coordinates": [144.95869339408225, -37.81105... 0 31.787580
109 {"coordinates": [144.95138857277198, -37.78077... 107426 55.825150
45 {"coordinates": [144.9295031556217, -37.794439... 0 2.826674
243 {"coordinates": [144.9693670992385, -37.803343... 0 10.914450
273 {"coordinates": [144.9479395483275, -37.802828... 0 13.532624
135 {"coordinates": [144.96674878780823, -37.80111... 0 5.228496
addressp_1 asset_clas asset_type objectid str_id \
293 0 Signage Sign - Public Transport 39748 1252536
8 78 Signage Sign - Public Transport 2922 1248743
30 200 Signage Sign - Public Transport 15210 1239404
308 285 Signage Sign - Public Transport 44101 1268402
289 239 Signage Sign - Public Transport 36816 1252743
109 306 Signage Sign - Public Transport 7176 1244570
45 299 Signage Sign - Public Transport 23024 1577042
243 117 Signage Sign - Public Transport 16758 1240396
273 123 Signage Sign - Public Transport 29192 1251207
135 179 Signage Sign - Public Transport 17860 1240314
addresspt asset_subt model_desc mcc_id \
293 0 NaN Sign - Public Transport 1 Panel 1252536
8 107419 NaN Sign - Public Transport 1 Panel 1248743
30 540076 NaN Sign - Public Transport 1 Panel 1239404
308 105393 NaN Sign - Public Transport 1 Panel 1268402
289 577288 NaN Sign - Public Transport 1 Panel 1252743
109 111342 NaN Sign - Public Transport 1 Panel 1244570
45 106320 NaN Sign - Public Transport 1 Panel 1577042
243 612989 NaN Sign - Public Transport 1 Panel 1240396
273 107985 NaN Sign - Public Transport 1 Panel 1251207
135 106109 NaN Sign - Public Transport 1 Panel 1240314
roadseg_id descriptio model_no
293 22508 Sign - Public Transport 1 Panel Bus Stop Type 8 P.16
8 22245 Sign - Public Transport 1 Panel Bus Stop Type 8 P.16
30 22466 Sign - Public Transport 1 Panel Bus Stop Type 8 P.16
308 20118 Sign - Public Transport 1 Panel Bus Stop Type 8 P.16
289 20026 Sign - Public Transport 1 Panel Bus Stop Type 8 P.16
109 0 Sign - Public Transport 1 Panel Bus Stop Type 8 P.16
45 21693 Sign - Public Transport 1 Panel Bus Stop Type 1 P.16
243 20556 Sign - Public Transport 1 Panel Bus Stop Type 3 P.16
273 21015 Sign - Public Transport 1 Panel Bus Stop Type 8 P.16
135 20530 Sign - Public Transport 1 Panel Bus Stop Type 3 P.16
Displaying Dataset Overview¶
This part of the code verifies the datasets by displaying their dimensions and a preview of the first few rows. It ensures that the datasets have been successfully loaded and are ready for analysis.
print('Coworking Space Dataset')
print(coworking_df.head())
print(coworking_df.info())
Coworking Space Dataset
organisation address \
0 25 King Collective 25 King Street, Melbourne VIC 3000
1 ACMI X Level 4, 2 Kavanagh Street, Southbank VIC 3006
2 GPT Space&Co Level 10 and 11, 550 Bourke Street, Melbourne ...
3 GPT Space&Co 181 William Street, Melbourne VIC 3000
4 Hub Parliament Station: 1 Nicholson Street, Melbo...
website latitude longitude \
0 http://www.25king.com.au/ -37.820000 144.957000
1 http://www.acmi.net.au/acmi-x -37.822000 144.968000
2 https://www.gpt.com.au/office/space-co -37.815608 144.957393
3 https://www.gpt.com.au/office/space-co -37.815000 144.958000
4 http://www.hubaustralia.com/ -37.808991 144.973448
geopoint
0 -37.81984, 144.95703
1 -37.8218014, 144.9675938
2 -37.81560843520474, 144.9573929798318
3 -37.81509510253944, 144.9576562644202
4 -37.8089914, 144.9734478
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 52 entries, 0 to 51
Data columns (total 6 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 organisation 52 non-null object
1 address 52 non-null object
2 website 52 non-null object
3 latitude 51 non-null float64
4 longitude 51 non-null float64
5 geopoint 51 non-null object
dtypes: float64(2), object(4)
memory usage: 2.6+ KB
None
The dataset contains total of six columns: organisation, address, website, latitude, longitude and geopoint. Organisation column shows the name of the coworking space, address column shows their addresses on the map, website column is their websites, latitude and longitude columns can be used later to navigate these coworking spaces on the map, and geopoint column is a combination of latitude and longitude columns.
print('Business Establishments Dataset')
print(business_df.head())
Business Establishments Dataset
census_year block_id property_id base_property_id clue_small_area \
0 2010 1 108843 108843 Melbourne (CBD)
1 2010 1 108843 108843 Melbourne (CBD)
2 2010 1 611393 611393 Melbourne (CBD)
3 2010 1 611394 611394 Melbourne (CBD)
4 2010 2 103973 103973 Melbourne (CBD)
trading_name business_address \
0 Vacant 74 Spencer Street MELBOURNE 3000
1 Vacant 44 Spencer Street MELBOURNE 3000
2 Peddle Thorp Architects Part Level 1, 525-541 Flinders Street MELBOURN...
3 Vacant 553-557 Flinders Street MELBOURNE 3000
4 Enterprize Park 469-503 Flinders Street MELBOURNE 3000
industry_anzsic4_code industry_anzsic4_description \
0 0 Vacant Space
1 0 Vacant Space
2 6921 Architectural Services
3 0 Vacant Space
4 8922 Nature Reserves and Conservation Parks Operation
longitude latitude point
0 144.956636 -37.821601 -37.82160071575, 144.9566363299689
1 144.956636 -37.821601 -37.82160071575, 144.9566363299689
2 144.957144 -37.820818 -37.82081814595, 144.95714412279256
3 144.956514 -37.820979 -37.8209790439, 144.95651375222326
4 144.958514 -37.820598 -37.8205978666, 144.95851393791304
The dataset contains total of 11 columns. They shows information of each building block on the map, together with their trading name, address, area and type of industry they operate in.
print(business_df.info())
<class 'pandas.core.frame.DataFrame'> RangeIndex: 393878 entries, 0 to 393877 Data columns (total 12 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 census_year 393878 non-null int64 1 block_id 393878 non-null int64 2 property_id 393878 non-null int64 3 base_property_id 393878 non-null int64 4 clue_small_area 393878 non-null object 5 trading_name 393751 non-null object 6 business_address 393877 non-null object 7 industry_anzsic4_code 393878 non-null int64 8 industry_anzsic4_description 393878 non-null object 9 longitude 389093 non-null float64 10 latitude 389093 non-null float64 11 point 389093 non-null object dtypes: float64(2), int64(5), object(5) memory usage: 36.1+ MB None
The above code shows the data type of each column in the business establishments dataset. Census year, Block ID, Property ID, Industry ANZSIC code are stored in integer type, to represent a specific year or ID numbers. On the other hand, longitude and latitude's data type is float, which presents decimal numbers of the location on the map. Lastly, other attributes are stored as object, which contains their names, addresses or industry descriptions.
print("Tram Stops Dataset")
print(tram_df.head())
Tram Stops Dataset
geo_point_2d \
0 -37.81922319307822, 144.9614014008424
1 -37.821539117626855, 144.95356912978238
2 -37.815426586135686, 144.94512063442602
3 -37.813414856197724, 144.94137823870162
4 -37.814591782869805, 144.94655055842398
geo_shape \
0 {"coordinates": [144.9614014008424, -37.819223...
1 {"coordinates": [144.95356912978238, -37.82153...
2 {"coordinates": [144.94512063442602, -37.81542...
3 {"coordinates": [144.94137823870162, -37.81341...
4 {"coordinates": [144.94655055842398, -37.81459...
name xorg stop_no mccid_str \
0 Market Street / Flinders Street GIS Team 3 NaN
1 Victoria Police Centre / Flinders Street GIS Team D6 NaN
2 Central Pier / Harbour Esplanade GIS Team D2 NaN
3 New Quay Promenade / Docklands Drive GIS Team D10 NaN
4 Etihad Statium / La Trobe Street GIS Team D1 NaN
xsource xdate mccid_int
0 Mapbase 2011-10-18 3
1 Mapbase 2011-10-18 6
2 Mapbase 2011-10-18 10
3 Mapbase 2011-10-18 11
4 Mapbase 2011-10-18 13
The dataset contains total of 8 columns. They shows information of each tram stop on the map, together with their street name, and type of tram stops they are.
print(tram_df.info())
<class 'pandas.core.frame.DataFrame'> RangeIndex: 28 entries, 0 to 27 Data columns (total 9 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 geo_point_2d 28 non-null object 1 geo_shape 28 non-null object 2 name 28 non-null object 3 xorg 28 non-null object 4 stop_no 28 non-null object 5 mccid_str 0 non-null float64 6 xsource 28 non-null object 7 xdate 28 non-null object 8 mccid_int 28 non-null int64 dtypes: float64(1), int64(1), object(7) memory usage: 2.1+ KB None
The above code shows the data type of each column in the Tram Stops dataset. Most of the attributes are stored as object, except for mccid_str and mccod_int which are some identifiers of the tram stops
print('Bus Stops Dataset')
print(bus_df.head())
Bus Stops Dataset
geo_point_2d \
0 -37.80384165792465, 144.93239283833262
1 -37.81548699581418, 144.9581794249902
2 -37.81353897396532, 144.95728334230756
3 -37.82191394843844, 144.95539345270072
4 -37.83316401267591, 144.97443745130263
geo_shape prop_id addresspt1 \
0 {"coordinates": [144.93239283833262, -37.80384... 0 76.819824
1 {"coordinates": [144.9581794249902, -37.815486... 0 21.561304
2 {"coordinates": [144.95728334230756, -37.81353... 0 42.177187
3 {"coordinates": [144.95539345270072, -37.82191... 0 15.860434
4 {"coordinates": [144.97443745130263, -37.83316... 0 0.000000
addressp_1 asset_clas asset_type objectid str_id \
0 357 Signage Sign - Public Transport 355 1235255
1 83 Signage Sign - Public Transport 600 1231226
2 207 Signage Sign - Public Transport 640 1237092
3 181 Signage Sign - Public Transport 918 1232777
4 0 Signage Sign - Public Transport 1029 1271914
addresspt asset_subt model_desc mcc_id \
0 570648 NaN Sign - Public Transport 1 Panel 1235255
1 548056 NaN Sign - Public Transport 1 Panel 1231226
2 543382 NaN Sign - Public Transport 1 Panel 1237092
3 103975 NaN Sign - Public Transport 1 Panel 1232777
4 0 NaN Sign - Public Transport 1 Panel 1271914
roadseg_id descriptio model_no
0 21673 Sign - Public Transport 1 Panel Bus Stop Type 13 P.16
1 20184 Sign - Public Transport 1 Panel Bus Stop Type 8 P.16
2 20186 Sign - Public Transport 1 Panel Bus Stop Type 8 P.16
3 22174 Sign - Public Transport 1 Panel Bus Stop Type 8 P.16
4 22708 Sign - Public Transport 1 Panel Bus Stop Type 8 P.16
The dataset contains total of 15 columns. They shows information of each bus stops on the map, together with their location, type of asset, and their identifying numbers.
print(bus_df.info())
<class 'pandas.core.frame.DataFrame'> RangeIndex: 309 entries, 0 to 308 Data columns (total 16 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 geo_point_2d 309 non-null object 1 geo_shape 309 non-null object 2 prop_id 309 non-null int64 3 addresspt1 309 non-null float64 4 addressp_1 309 non-null int64 5 asset_clas 309 non-null object 6 asset_type 309 non-null object 7 objectid 309 non-null int64 8 str_id 309 non-null int64 9 addresspt 309 non-null int64 10 asset_subt 0 non-null float64 11 model_desc 309 non-null object 12 mcc_id 309 non-null int64 13 roadseg_id 309 non-null int64 14 descriptio 309 non-null object 15 model_no 309 non-null object dtypes: float64(2), int64(7), object(7) memory usage: 38.8+ KB None
The above code shows the data type of each column in the Bus Stops dataset. Most of the attributes are stored as object, except for attributes that represent their ID numbers, which are stored as integer and float.
Data Cleaning and Processing¶
Check for missing values and Duplicate Values¶
This section performs a data quality check by identifying missing values and duplicate rows in the coworking spaces, business establishments, tram stops and bus stops datasets. This helps ensure the data is clean and ready for further analysis by highlighting potential issues that need to be addressed.
- Coworking Spaces Dataset
# Check if there are any missing values in the datasets
print(coworking_df.isnull().sum())
#Check if there are any duplicates in the datasets
print(f"\nDuplicate rows in Coworking Spaces Dataset: {coworking_df.duplicated().sum()}\n") # Check for duplicates in the dataset and print the count of duplicates.
organisation 0 address 0 website 0 latitude 1 longitude 1 geopoint 1 dtype: int64 Duplicate rows in Coworking Spaces Dataset: 0
From the above code, it can be seen that the Coworking Space dataset contains no duplicate rows.
- Business Establishments Dataset
# Check if there are any missing values in the datasets
print(business_df.isnull().sum())
#Check if there are any duplicates in the datasets
print(f"\nDuplicate rows in Business Establishments Dataset: {business_df.duplicated().sum()}\n") # Check for duplicates in the dataset and print the count of duplicates.
census_year 0 block_id 0 property_id 0 base_property_id 0 clue_small_area 0 trading_name 127 business_address 1 industry_anzsic4_code 0 industry_anzsic4_description 0 longitude 4785 latitude 4785 point 4785 dtype: int64 Duplicate rows in Business Establishments Dataset: 0
From the above code, it can be seen that the Business Establishments dataset contains no duplicate rows.
- City Tram Stops Dataset
# Check if there are any missing values in the datasets
print(tram_df.isnull().sum())
#Check if there are any duplicates in the datasets
print(f"\nDuplicate rows in Tram Stops Dataset: {tram_df.duplicated().sum()}\n") # Check for duplicates in the dataset and print the count of duplicates.
geo_point_2d 0 geo_shape 0 name 0 xorg 0 stop_no 0 mccid_str 28 xsource 0 xdate 0 mccid_int 0 dtype: int64 Duplicate rows in Tram Stops Dataset: 0
From the above code, it can be seen that the City Tram Stops dataset contains no duplicate rows.
- Bus Stops Dataset
# Check if there are any missing values in the datasets
print(bus_df.isnull().sum())
#Check if there are any duplicates in the datasets
print(f"\nDuplicate rows in Metro Stations Dataset: {bus_df.duplicated().sum()}\n")
geo_point_2d 0 geo_shape 0 prop_id 0 addresspt1 0 addressp_1 0 asset_clas 0 asset_type 0 objectid 0 str_id 0 addresspt 0 asset_subt 309 model_desc 0 mcc_id 0 roadseg_id 0 descriptio 0 model_no 0 dtype: int64 Duplicate rows in Metro Stations Dataset: 0
From the above code, it can be seen that the City Bus Stops dataset contains no duplicate rows.
Handling Missing and Duplicate Data¶
This section addresses missing values in the coworking space, business establishments and city tram stops datasets.
- For Coworking Spaces Dataset: Any Coworking space without detail location is removed, redundant "geopoint" column is removed to streamline for the analysis.
- For Business Establishments Dataset: Remove any location without clear latitude and longitude, irrelevant and redundant columns are removed (Block ID, Property ID, Industry (ANZSIC4) code, Point)
- For Tram Stop Dataset: Remove irrelevant and redundant columns (Geo Shape, xorg, stop_no, mccid_str, xsource, xdate, mccid_int)
- For Metro Train Stations Dataset: Remove irrelevant and redundant columns (Geo Shape, he_loop, lift, pids)
# Handle missing values
coworking_clean = coworking_df.dropna(subset=['latitude', 'longitude'])
business_clean = business_df.dropna(subset=['trading_name', 'latitude', 'longitude'])
# Check if there are any missing values in the cleaned dataset
print(coworking_clean.isnull().sum())
print(business_clean.isnull().sum())
organisation 0 address 0 website 0 latitude 0 longitude 0 geopoint 0 dtype: int64 census_year 0 block_id 0 property_id 0 base_property_id 0 clue_small_area 0 trading_name 0 business_address 1 industry_anzsic4_code 0 industry_anzsic4_description 0 longitude 0 latitude 0 point 0 dtype: int64
From the above table, it can be seen that Coworking Space dataset and Business Establishments dataset no longer contains any missing values.
# Remove irrelevant and redundant columns
coworking_clean = coworking_clean.drop(columns=['geopoint'])
business_clean = business_clean.drop(columns=['block_id','property_id','base_property_id','industry_anzsic4_code','point'])
tram_clean = tram_df.drop(columns=['geo_shape','xorg','stop_no','mccid_str','xsource','xdate','mccid_int'])
bus_clean = bus_df[['geo_point_2d']]
Redundant columns are removed from the table. Only relevant attributes are kept for EDA and visualisations.
Feature Engineering¶
This section split composite fields of Tram Stop Dataset and Bus Stop Dataset's Geo Point column into separate longitude and latitude columns. Then the Geo Point column is dropped to avoid redundancy.
tram_clean[['latitude', 'longitude']] = tram_clean['geo_point_2d'].str.split(',', expand=True).astype(float)
tram_clean = tram_clean.drop(columns=['geo_point_2d'])
bus_clean[['latitude', 'longitude']] = bus_clean['geo_point_2d'].str.split(',', expand=True).astype(float)
bus_clean = bus_clean.drop(columns=['geo_point_2d'])
C:\Users\phuon\AppData\Local\Temp\ipykernel_17812\3441797288.py:3: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead
See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
bus_clean[['latitude', 'longitude']] = bus_clean['geo_point_2d'].str.split(',', expand=True).astype(float)
C:\Users\phuon\AppData\Local\Temp\ipykernel_17812\3441797288.py:3: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead
See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
bus_clean[['latitude', 'longitude']] = bus_clean['geo_point_2d'].str.split(',', expand=True).astype(float)
From the Geo Point columns, latitude and longitude are separated by a comma. Therefore, two separate columns for latitude and longitude are created, then the Geo Point column is dropped to avoid redundancy.
Verify Data Cleaning¶
print(coworking_clean.info())
print(business_clean.info())
<class 'pandas.core.frame.DataFrame'> Index: 51 entries, 0 to 51 Data columns (total 5 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 organisation 51 non-null object 1 address 51 non-null object 2 website 51 non-null object 3 latitude 51 non-null float64 4 longitude 51 non-null float64 dtypes: float64(2), object(3) memory usage: 2.4+ KB None <class 'pandas.core.frame.DataFrame'> Index: 388998 entries, 0 to 393877 Data columns (total 7 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 census_year 388998 non-null int64 1 clue_small_area 388998 non-null object 2 trading_name 388998 non-null object 3 business_address 388997 non-null object 4 industry_anzsic4_description 388998 non-null object 5 longitude 388998 non-null float64 6 latitude 388998 non-null float64 dtypes: float64(2), int64(1), object(4) memory usage: 23.7+ MB None
The information of the Coworking Space and Business Establishments datasets are extracted to verify their data type, list of attributes and number of instances. From the above information, all attributes are stored in suitable data types, only relevant columns are kept, number of instances that are not null can be seen.
print(tram_clean.info())
print(bus_clean.info())
<class 'pandas.core.frame.DataFrame'> RangeIndex: 28 entries, 0 to 27 Data columns (total 3 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 name 28 non-null object 1 latitude 28 non-null float64 2 longitude 28 non-null float64 dtypes: float64(2), object(1) memory usage: 804.0+ bytes None <class 'pandas.core.frame.DataFrame'> RangeIndex: 309 entries, 0 to 308 Data columns (total 2 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 latitude 309 non-null float64 1 longitude 309 non-null float64 dtypes: float64(2) memory usage: 5.0 KB None
Similarly, Tram Stops and Bus Stops datasets are examined with their locations on the map.
From the above tables, the structures of all four datasets are clear with sufficient information, no duplicates, only one missing value of business address, which can be left blank on the map.
Data Subsetting¶
In this section, the Business Establishments Dataset will be subsetted to filter the types of businesses that would likely be of interest to coworking space users - including cafe, convenient stores, fitness centres and more. The filtered businesses are divided into three major groups: Food & Beverage, Daily Essentials and Personal Services & Wellbeing. Additionally, only the latest year of data is used, which means census year column is filtered to 2023.
business_clean_2023 =business_clean[business_clean['census_year']==2023]
The above code filter only the census year is 2023.
category_map = {
# Food & Beverage
"Cafes and Restaurants": "Food & Beverage",
"Takeaway Food Services": "Food & Beverage",
"Pubs, Taverns and Bars": "Food & Beverage",
# Daily Essentials
"Pharmacy": "Retail & Essentials",
"Newsagent and Book Retailing": "Retail & Essentials",
"Newspaper and Book Retailing": "Retail & Essentials",
"Stationery Goods Retailing": "Retail & Essentials",
"Liquor Retailing": "Retail & Essentials",
"Fruit and Vegetable Retailing": "Retail & Essentials",
"Supermarket and Grocery Stores": "Retail & Essentials",
"Convenience Store": "Retail & Essentials",
"Catering Services": "Retail & Essentials",
# Personal Services & Wellbeing
"Hairdressing and Beauty Services": "Wellbeing & Services",
"Health and Fitness Centres and Gymnasia Operation": "Wellbeing & Services",
"Physiotherapy Services": "Wellbeing & Services",
"Alternative Health Services": "Wellbeing & Services",
"Chiropractic and Osteopathic Services": "Wellbeing & Services",
}
filtered_business_df = business_clean_2023[
business_clean_2023["industry_anzsic4_description"].isin(category_map.keys())
]
filtered_business_df["business_category"] = filtered_business_df["industry_anzsic4_description"].map(category_map)
C:\Users\phuon\AppData\Local\Temp\ipykernel_17812\2125868158.py:29: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_indexer,col_indexer] = value instead See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy filtered_business_df["business_category"] = filtered_business_df["industry_anzsic4_description"].map(category_map)
filtered_business_df[['business_category']].value_counts()
business_category Food & Beverage 2481 Wellbeing & Services 520 Retail & Essentials 370 Name: count, dtype: int64
From the code, three categories of Business Establishments have been created namely: Food & Beverage, Daily Essentials, Personal Services and Wellbeing. Food & Beverage contain all cafes and restaurants, bars, takeaway food, which are suitable for those who need to grab a bite or a cafe during or afterwork. Daily Essentials contain all pharmacies and retailling stores, supermarkets and convenient stores, which are essential for those who need to do groceries shopping after work. Lastly, Personal Service & Wellbeing includes all services for health and wellbeing like gym, salons, health services.
- Business establishments have been divided into 3 sub-categories that are relevant to coworking spaces users
- From the 3 categories, most are Food & Beverage with 2.4 thousands local businesses. The rest include Wellbeing & Services (520 businesses) and Retail & Essentials (370 businesses)
Data Exploration and Visualisation¶
Visualisation of Businesses in Melbourne CBD¶
This code generates a scatter plot that visually represents the spatial distribution of business services relevant to coworking space users in Melbourne.
# Define color mapping for each category
color_map = {
"Food & Beverage": "blue",
"Retail & Essentials": "green",
"Wellbeing & Services": "orange",
}
# Create the plot
plt.figure(figsize=(10, 8))
for category, color in color_map.items():
subset = filtered_business_df[filtered_business_df['business_category'] == category]
plt.scatter(subset['longitude'], subset['latitude'], c=color, label=category, alpha=0.6, edgecolors='k', s=40)
plt.xlabel("Longitude")
plt.ylabel("Latitude")
plt.title("Relevant Businesses for Coworking Space Users in Melbourne")
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()
- Business Density is Highly Concentrated in the CBD
The highest concentration of all three service categories appears within a tight, centralized cluster. This suggests that the Melbourne Central Business District (CBD) offers the most diverse and abundant mix of amenities that are attractive or essential to coworking space users, such as cafes, grocery stores, gyms, and wellness services.
- Food & Beverage Services Are the Most Widely Distributed
The blue dots representing Food & Beverage businesses are more evenly spread across the map, reaching further into surrounding areas beyond the core cluster. This indicates that access to cafes and restaurants is relatively widespread, supporting flexible workspace users who may not be located in the immediate CBD.
- Retail & Essentials and Wellbeing Services Are More Clustered
Both green (Retail & Essentials) and orange (Wellbeing & Services) markers are more densely packed in central zones, with limited presence in outlying areas. This suggests these services tend to follow higher foot traffic and population density, reinforcing the value of placing coworking spaces near mixed-use commercial zones.
- Ideal Coworking Locations Align with Amenity Hotspots
For planners and investors, the overlapping clusters of these business types point to highly serviced areas that may support additional coworking infrastructure. These are places where workers have convenient access to meals, daily necessities, and health services—factors that directly influence coworking space desirability and usage.
Visualisation of tram stops and bus stops¶
This code creates a scatter plot that visualizes the geographic locations of tram stops, bus stops, and coworking spaces in Melbourne’s Central Business District (CBD). Each type of location is represented with a different shape and color, making it easy to distinguish between them on the map.
# Plot the data
plt.figure(figsize=(10, 8))
plt.scatter(bus_clean['longitude'], bus_clean['latitude'],
marker='o', color='red', label='Bus Stops', s=100)
plt.scatter(tram_clean['longitude'], tram_clean['latitude'],
marker='s', color='blue', label='Tram Stops', s=100)
plt.scatter(coworking_clean['longitude'], coworking_clean['latitude'],
marker='P', color='green', label='Coworking Spaces', s=100)
plt.xlabel("Longitude")
plt.ylabel("Latitude")
plt.title("Tram Stops, Bus Stops and Coworking Spaces in the CBD")
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()
- Coworking spaces cluster densely in the tram-accessible area
Most coworking spaces appear to be concentrated in a relatively tight cluster that closely overlaps with the tram stop network. This suggests that tram accessibility is a major factor in the location of coworking hubs, likely because trams offer a reliable and central mode of transport for inner-city commuters.
- Bus stops are widely distributed beyond the coworking core
Bus stops are more broadly scattered across the map, covering peripheral and suburban areas of the CBD and beyond. However, there is less overlap between bus stop locations and coworking spaces, which implies that bus networks may serve a different commuter segment, or that they are used more to support surrounding residential zones rather than coworking precincts.
- Multi-modal transport connectivity is strongest in the coworking zone
The zone with the highest density of coworking spaces is also well-covered by both tram and bus networks, indicating multi-modal transport access. This connectivity is likely a strategic advantage for coworking space operators, catering to a variety of commuter preferences and enhancing location appeal.
- Opportunity for expansion may exist in well-connected but underutilized zones
In areas where tram or bus stops are present without many coworking spaces nearby, there may be untapped potential for new coworking developments. City planners and investors could explore these areas as candidates for strategic expansion, especially if other amenities are also present (e.g., food, retail, gyms).
Visualisation of Coworking Spaces on the Map¶
# Average lat/lon for center
melbourne_center = [coworking_clean['latitude'].mean(), coworking_clean['longitude'].mean()]
coworking_map = folium.Map(location=melbourne_center, zoom_start=15)
# Add coworking space markers
for _, row in coworking_clean.iterrows():
folium.Marker(
location=[row['latitude'], row['longitude']],
tooltip=f"<b>{row['organisation']}</b><br>{row['address']}<br>{row['website']}",
icon=folium.Icon(color='blue', icon='briefcase', prefix='fa')
).add_to(coworking_map)
# Display the map
coworking_map
From the above map, all Coworking Spaces in the CBD are highlighted in blue color. When users click on these markers, coworking spaces adresses and their websites can be seen. It is noticeable that coworking spaces are centered in particular areas in CBD, while there could be high demands for coworking spaces in the Southern suburbs and Northern suburbs as well. These are also areas with high density of local services. If coworking spaces are more spread out, these local services can benefit from remote-working customers.
Visualisation of Coworking Spaces and Nearby Services on the Map¶
Next, a map for all coworking spaces users will be created. The aim of this map is for user to quickly find a coworking space and also can view its nearby public transportation (bus stops and tram stops), cafe/restaurants, supermarkets or wellbeing services. To make it more interactive, user can choose which service are they looking for by clicking on a toggle with layers of services. For example, if they are looking for a nearby bus stop, they just need to choose "Bus Stops" from the map and they can see all bus stops and how close they are to each coworking spcae.
# Center map on Melbourne
coworking_full_map = folium.Map(location=melbourne_center, zoom_start=15)
# Create main toggleable group
main_group = folium.FeatureGroup(name="Nearby Services", show=True).add_to(coworking_full_map)
# Coworking space markers (not clustered)
for _, row in coworking_clean.iterrows():
folium.Marker(
location=[row['latitude'], row['longitude']],
tooltip=f"<b>{row['organisation']}</b><br>{row['address']}<br>{row['website']}",
icon=folium.Icon(color='blue', icon='briefcase', prefix='fa')
).add_to(coworking_full_map)
This section of the code creates the base map that centers on Melbourne and plots each coworking space as a blue marker with a briefcase icon. The map is initialized using the average latitude and longitude of all coworking spaces to ensure it’s centered correctly, and a zoom level of 15 is used to provide a street-level view. A toggleable layer group named “Nearby Services” is also created in preparation for adding public transport and business service layers later on.
The core of this section is a loop that goes through each coworking space in the dataset and places a marker at its exact location. Each marker includes a tooltip that displays the organisation’s name, address, and website when hovered over. This provides users with a clear and informative visual overview of coworking space options in Melbourne, forming the foundation for more detailed exploration of surrounding amenities.
# Bus stops
bus_group = FeatureGroupSubGroup(main_group, "Bus Stops", show=False)
coworking_full_map.add_child(bus_group)
for _, row in bus_clean.iterrows():
folium.Marker(
location=[row['latitude'], row['longitude']],
popup="Bus Stop",
icon=folium.Icon(color='green', icon='bus', prefix='fa')
).add_to(bus_group)
# Tram stops
tram_group = FeatureGroupSubGroup(main_group, "Tram Stops", show=False)
coworking_full_map.add_child(tram_group)
for _, row in tram_clean.iterrows():
folium.Marker(
location=[row['latitude'], row['longitude']],
popup="Tram Stop",
icon=folium.Icon(color='red', icon='subway', prefix='fa')
).add_to(tram_group)
This section of the code adds layers of public transport information—specifically bus stops and tram stops—to the interactive coworking map. It begins by creating a subgroup called "Bus Stops" under the main toggleable group "Nearby Services". This subgroup is not shown by default (show=False), meaning the bus stop markers will only appear if the user actively selects that layer. The code then goes through each bus stop in the dataset (bus_clean) and adds a green marker with a bus icon at the appropriate location. E
Each marker includes a simple popup label that says “Bus Stop.” The same approach is applied for tram stops: a separate subgroup called "Tram Stops" is created, also hidden by default, and then red markers with subway icons are placed for each tram stop listed in the dataset (tram_clean). These layers allow users to toggle transport options on the map, making it easier to assess the accessibility of coworking spaces via public transit, which is highly relevant for remote workers, commuters, and urban planners.
# Business categories
category_icons = {
"Food & Beverage": "utensils",
"Retail & Essentials": "store",
"Wellbeing & Services": "dumbbell",
}
for category, icon in category_icons.items():
category_group = FeatureGroupSubGroup(main_group, category, show=False)
coworking_full_map.add_child(category_group)
subset = filtered_business_df[filtered_business_df['business_category'] == category]
for _, row in subset.iterrows():
folium.Marker(
location=[row['latitude'], row['longitude']],
popup=f"<b>{row['trading_name']}</b><br>{row['business_address']}",
icon=folium.Icon(color='gray', icon=icon, prefix='fa')
).add_to(category_group)
This portion of the code adds business establishments to the map, grouped into three meaningful categories: Food & Beverage, Retail & Essentials, and Wellbeing & Services. Each category is represented with an intuitive Font Awesome icon—for example, utensils for food, a store icon for retail, and a dumbbell for health and wellness services. For each category, a corresponding subgroup is created under the “Nearby Services” layer, but hidden by default (show=False) so that users can choose to view them only when needed. The code then filters the dataset (filtered_business_df) to select businesses that belong to each category and plots them as gray markers on the map.
Each marker shows a popup with the business’s name and address. This feature allows users—particularly remote workers and city planners—to explore what types of services surround coworking spaces, such as nearby cafes, gyms, and convenience stores, helping them assess how suitable and well-supported each location might be.
# Layer control
folium.LayerControl(collapsed=False).add_to(coworking_full_map)
#legend with icons
legend_html = """
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<div style="position: fixed;
bottom: 50px; left: 50px; width: 300px; height: 180px;
background-color: white; border:2px solid grey; z-index:1000; font-size:14px;
padding: 10px;">
<b>Legend</b><br><br>
<i class="fas fa-utensils" style="color:red;"></i> Food & Beverage<br>
<i class="fas fa-store" style="color:red;"></i> Retail & Essentials<br>
<i class="fas fa-dumbbell" style="color:red;"></i> Wellbeing & Services<br>
<i class="fas fa-briefcase" style="color:blue;"></i> Coworking Spaces<br>
<i class="fas fa-bus" style="color:green;"></i> Bus Stops<br>
<i class="fas fa-subway" style="color:red;"></i> Tram Stops<br>
</div>
"""
coworking_full_map.get_root().html.add_child(folium.Element(legend_html))
# Display the map
coworking_full_map
This final section of the code adds two key user interface features to the interactive coworking map: a layer control toggle and a visual legend using icons. First, folium.LayerControl(collapsed=False) is added, which enables a menu on the top-right corner of the map. This menu allows users to selectively show or hide different layers—such as bus stops, tram stops, and business categories—making it easy to explore the map based on personal interests or planning needs.
Next, the code defines a custom HTML block that creates a fixed-position legend at the bottom-left corner of the screen. This legend uses Font Awesome icons and colors to clearly explain what each symbol on the map represents: red icons for food, retail, and wellbeing services; a blue briefcase for coworking spaces; and transport icons for bus and tram stops. The legend helps users quickly interpret the markers on the map without needing prior knowledge, enhancing the map’s accessibility and user experience. Finally, the map is displayed with all these interactive and visual features integrated, resulting in a powerful tool for discovering and evaluating coworking spaces and nearby services in Melbourne.
Clustered Map of Coworking Spaces and Nearby Services Density¶
This section of the use case creates an interactive map designed to help city planners and coworking space investors make informed, data-driven decisions about where to expand or invest. The map visualises the locations of existing coworking spaces in Melbourne, and adds clustered layers for key nearby services—such as public transport (bus and tram stops) and local businesses in categories like food, retail, and wellbeing.
By using marker clusters, the map efficiently handles dense areas, grouping nearby service points into expandable clusters that reveal detailed information upon zoom or click. This allows users to quickly assess the intensity of nearby amenities and accessibility via public transport around each coworking space.
The overall goal is to identify high-potential areas for coworking expansion—places that are not only well connected but also supported by a rich mix of services that attract freelancers, entrepreneurs, and remote workers. This visualisation supports strategic urban development, business planning, and community integration initiatives.
# Center the map on Melbourne
melbourne_center = [coworking_clean['latitude'].mean(), coworking_clean['longitude'].mean()]
folium_map = folium.Map(location=melbourne_center, zoom_start=13)
# MarkerCluster to group all toggled layers
cluster_group = folium.FeatureGroup(name="Nearby Services", show=True).add_to(folium_map)
master_cluster = MarkerCluster().add_to(cluster_group)
This snippet of code sets up the foundational structure for creating a clean and interactive map of Melbourne that supports marker clustering. It begins by calculating the geographic center of all coworking spaces based on their latitude and longitude values, ensuring that the map is centered appropriately over the city. The map is then initialized with zoom_start=13, which provides a good city-wide view of Melbourne while still allowing enough detail to identify neighborhoods.
Next, a FeatureGroup named "Nearby Services" is added to the map. This group will later be used to organize and toggle layers that show nearby transport and business services. Importantly, a MarkerCluster is added to this group, which allows many map markers to be grouped visually. Rather than displaying hundreds of overlapping markers, the cluster groups them together into a single expandable icon, improving clarity and performance. When users zoom in or click on a cluster, it expands to reveal individual locations. This is a crucial step in making the map user-friendly, especially when dealing with dense areas like the Melbourne CBD where coworking spaces and services are tightly packed.
# Add coworking spaces (always visible)
coworking_group = folium.FeatureGroup(name="Coworking Spaces", show=True).add_to(folium_map)
for _, row in coworking_clean.iterrows():
folium.Marker(
location=[row['latitude'], row['longitude']],
popup=f"<b>{row['organisation']}</b><br>{row['address']}",
icon=folium.Icon(color='blue', icon='briefcase', prefix='fa')
).add_to(coworking_group)
This part of the code adds coworking spaces to the map as individual markers, allowing users to see exactly where these workspaces are located across Melbourne. A FeatureGroup named "Coworking Spaces" is created so that this layer can be toggled on or off using the map’s layer control. Unlike other service layers, coworking spaces are set to be always visible by default (show=True), since they are the central focus of the map.
The code then loops through each coworking space in the dataset and adds a blue marker at its location, styled with a briefcase icon to visually represent professional or work-related spaces. When a user clicks on a marker, a popup displays the name and address of the coworking space, offering useful context for evaluation.
This layer gives city planners and investors a clear visual reference of the existing coworking infrastructure, which serves as the anchor points for evaluating nearby service density and accessibility.
# Add bus stops
bus_group = FeatureGroupSubGroup(master_cluster, "Bus Stops")
folium_map.add_child(bus_group)
for _, row in bus_clean.iterrows():
folium.Marker(
location=[row['latitude'], row['longitude']],
popup=f"{'Bus Stop'}",
icon=folium.Icon(color='green', icon='bus', prefix='fa')
).add_to(bus_group)
# Add tram stops
tram_group = FeatureGroupSubGroup(master_cluster, "Tram Stops")
folium_map.add_child(tram_group)
for _, row in tram_clean.iterrows():
folium.Marker(
location=[row['latitude'], row['longitude']],
popup=f"{'Tram Stop'}",
icon=folium.Icon(color='red', icon='subway', prefix='fa')
).add_to(tram_group)
This section of the code adds two essential public transport layers—bus stops and tram stops—to the map, grouped under the master_cluster for interactive clustering and improved visibility. Each type of transport stop is organised into its own FeatureGroupSubGroup, allowing users to selectively toggle them on or off using the map’s layer control.
The first block creates a layer for bus stops. It loops through the dataset, and for each stop, it places a green marker with a bus icon at the appropriate location. Each marker includes a popup that labels it simply as “Bus Stop.”
The second block follows the same pattern for tram stops, using the dataset tram_clean. It assigns each tram stop a red marker with a subway icon and adds it to a separate tram layer group.
By clustering these markers under master_cluster, the map remains clean and easy to navigate—even in dense areas—while still offering detailed access to public transport infrastructure. For city planners and coworking space investors, these layers are crucial in assessing transport accessibility, which is a major factor in determining the suitability of a location for new coworking development.
# Add business categories using FeatureGroupSubGroups
category_icons = {
"Food & Beverage": "utensils",
"Retail & Essentials": "store",
"Wellbeing & Services": "dumbbell",
}
for category, icon in category_icons.items():
group = FeatureGroupSubGroup(master_cluster, category)
folium_map.add_child(group)
subset = filtered_business_df[filtered_business_df['business_category'] == category]
for _, row in subset.iterrows():
folium.Marker(
location=[row['latitude'], row['longitude']],
popup=f"<b>{row['trading_name']}</b><br>{row['business_address']}",
icon=folium.Icon(color='grey', icon=icon, prefix='fa')
).add_to(group)
C:\Users\phuon\AppData\Local\Temp\ipykernel_17812\3073515618.py:16: UserWarning: color argument of Icon should be one of: {'darkgreen', 'pink', 'darkpurple', 'lightblue', 'lightgreen', 'beige', 'purple', 'red', 'blue', 'white', 'black', 'orange', 'darkred', 'lightgray', 'darkblue', 'green', 'lightred', 'cadetblue', 'gray'}.
icon=folium.Icon(color='grey', icon=icon, prefix='fa')
This section of the code adds local business establishments to the map, grouped by service type to support more meaningful exploration. It uses three main business categories: Food & Beverage, Retail & Essentials, and Wellbeing & Services, each paired with a relevant Font Awesome icon (such as utensils, store, and dumbbell). For each category, the code creates a separate FeatureGroupSubGroup under the master_cluster, allowing users to toggle them individually in the map’s layer control.
The code then filters the filtered_business_df dataset to select only businesses that match the current category. For each business, it places a gray marker on the map with the appropriate icon and includes a popup showing the trading name and address of the establishment. These markers are added into the corresponding sub-group, making it possible to visualize different types of services one layer at a time.
By organising business markers in this way, the map becomes a powerful tool for city planners and coworking investors to evaluate the density and type of nearby services. This is especially useful for identifying high-potential areas where essential amenities already exist—such as restaurants, grocery stores, and fitness centers—which tend to attract remote workers and support a thriving coworking ecosystem.
# Add Layer Control
folium.LayerControl(collapsed=False).add_to(folium_map)
# Add a legend (customized with HTML)
legend_html = """
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<div style="position: fixed;
bottom: 50px; left: 50px; width: 300px; height: 180px;
background-color: white; border:2px solid grey; z-index:1000; font-size:14px;
padding: 10px;">
<b>Legend</b><br><br>
<i class="fas fa-utensils" style="color:red;"></i> Food & Beverage<br>
<i class="fas fa-store" style="color:red;"></i> Retail & Essentials<br>
<i class="fas fa-dumbbell" style="color:red;"></i> Wellbeing & Services<br>
<i class="fas fa-briefcase" style="color:blue;"></i> Coworking Spaces<br>
<i class="fas fa-bus" style="color:green;"></i> Bus Stops<br>
<i class="fas fa-subway" style="color:red;"></i> Tram Stops<br>
</div>
"""
folium_map.get_root().html.add_child(folium.Element(legend_html))
# Show map
folium_map
This final block of code enhances the usability and clarity of the map by adding two important user interface features: a layer control toggle and a custom legend using intuitive icons. First, folium.LayerControl(collapsed=False) adds a control panel to the map interface, typically located in the top-right corner. This panel allows users to selectively show or hide different layers—such as coworking spaces, business categories, tram stops, and bus stops—making the map interactive and customisable based on the viewer's needs.
Next, the legend_html block defines a visually styled legend using HTML and Font Awesome icons. Positioned at the bottom-left of the map, the legend explains what each icon on the map represents.This legend improves accessibility, making it easy for users—especially city planners and investors—to quickly interpret what they’re seeing on the map without needing prior technical knowledge. Together, the layer control and icon-based legend create a user-friendly interface that supports strategic exploration of Melbourne’s coworking landscape and its surrounding services.
Conclusion¶
This use case demonstrates how spatial data visualisation can support data-driven decisions in urban planning and coworking space investment. By integrating the locations of coworking spaces with nearby public transport infrastructure (tram and bus stops) and essential business services (food, retail, wellbeing), the interactive map and supporting plots provide valuable insights into the accessibility and amenity density across Melbourne’s urban landscape.
Observations¶
- Coworking spaces are highly concentrated in Melbourne's CBD, closely aligned with dense clusters of tram stops and essential services. This suggests that accessibility and surrounding amenities play a major role in site selection and user demand.
- Tram stops show the strongest overlap with coworking locations, indicating that tram accessibility may be more influential than bus connectivity in supporting daily commuting for coworking users.
- Food & Beverage services are the most widely distributed, providing consistent support across a broader area, while Retail & Essentials and Wellbeing & Services tend to cluster around the CBD.
- Several areas with high transport accessibility (especially bus-heavy corridors) lack nearby coworking spaces, pointing to potential gaps in service coverage.
Recommendations¶
Based on the analysis of coworking spaces, public transport, and surrounding business services in Melbourne, the following recommendations are proposed for city planners, urban developers, and coworking space investors:
Prioritise Expansion in Transit-Rich Areas Focus new coworking developments in areas with high tram and bus stop density, especially where service clusters (e.g., food, fitness, retail) already exist but coworking supply is limited. These locations offer strong accessibility and are likely to attract mobile professionals.
Leverage Amenity Clusters to Maximise Attractiveness Coworking locations surrounded by a mix of food & beverage, retail, and wellbeing services are more appealing to users. Prioritise sites where these amenities co-locate to support convenience, work-life balance, and productivity.
Use Underserved Transit Corridors as Pilot Zones Identify bus corridors or fringe-tram zones with existing business density but few coworking options. These could serve as low-risk pilot locations to test demand and validate expansion strategies outside the CBD.
Improve Visibility with Interactive Mapping Tools Deploy an internal or public-facing interactive map dashboard to visualise coworking supply, transit links, and business environments. This would enable data-backed planning and stakeholder engagement across departments and investment teams.
Engage in Public–Private Partnerships (PPP) Coordinate with local councils and transport agencies to explore incentives, co-location models, or zoning flexibility that supports coworking developments in strategic urban nodes.
Monitor Business Ecosystem Growth Establish a monitoring framework to track the emergence of new services, startup clusters, and digital hubs—these are often early indicators of rising demand for coworking spaces in secondary neighborhoods.
Ensure Equity in Access While the CBD is well-served, extend efforts to promote coworking options in growth corridors and outer suburbs to support remote workers, students, and SMEs with limited central access.
By applying these recommendations, stakeholders can make strategic, data-informed decisions that not only meet current demand but also future-proof Melbourne's coworking ecosystem as part of a vibrant, accessible, and inclusive urban economy.